package middlewares

import (
	"bytes"
	"encoding/base64"
	"math"
	"sort"
	"strconv"
	"time"

	"gitee.com/xxhsjp/x-core/conf"
	"gitee.com/xxhsjp/x-core/utils/xcrypto"
	"gitee.com/xxhsjp/x-core/utils/xencoding"
	"github.com/gin-gonic/gin"

	"gitee.com/xxhsjp/x-core/http/result"
	"gitee.com/xxhsjp/x-web/httputils"
)

func ValidateSignature() gin.HandlerFunc {

	return func(ctx *gin.Context) {
		querys := httputils.GetQueryParams(ctx)

		var appCode string
		if appCodeTemp, ok := querys["appCode"]; ok {
			appCode = appCodeTemp
		} else {
			ctx.AbortWithStatusJSON(400, result.Failure("1003", "appCode is empty"))
			return
		}

		var uuid string
		if uuidTemp, ok := querys["uuid"]; ok {
			uuid = uuidTemp
		} else {
			ctx.AbortWithStatusJSON(400, result.Failure("1003", "uuid is empty"))
			return
		}

		var ts string
		if tsTemp, ok := querys["ts"]; ok {
			ts = tsTemp
		} else {
			ctx.AbortWithStatusJSON(400, result.Failure("1003", "ts is empty"))
			return
		}

		if sign, ok := querys["sign"]; ok {
			str := "appCode=" + appCode + "&uuid=" + uuid + "&ts=" + ts + "&appSecret=" + secretString(appCode)
			md5val := xencoding.Md5str(str)

			if sign != md5val {
				ctx.AbortWithStatusJSON(400, result.Failure("1002", "sign is invalid"))
				return
			}
		} else {
			ctx.AbortWithStatusJSON(400, result.Failure("1003", "sign is empty"))
			return
		}

		if ts, ok := querys["ts"]; ok {
			if unixts, err := strconv.Atoi(ts); err != nil {
				ctx.AbortWithStatusJSON(400, result.Failure("1004", err.Error()))
			} else {
				now := time.Now().Unix()
				diff := now - int64(unixts)
				gap := math.Abs(float64(diff))
				if int(gap) > 3600 {
					ctx.AbortWithStatusJSON(400, result.Failure("1005", "ts is expired"))
					return
				}
			}
		} else {
			ctx.AbortWithStatusJSON(400, result.Failure("1006", "ts is empty"))
			return
		}

		ctx.Next()

	}
}

func Encrypto_test(encrypted string) string {

	data := xcrypto.Aes.EncryptCBC([]byte(encrypted), secretBytes())
	decoded := base64.StdEncoding.EncodeToString(data)

	return decoded
}

func secretBytes() []byte {
	var secret = secretString("")
	return []byte(secret)
}

func secretString(appCode string) string {
	decryptedString, err := conf.Wrapper.GetDecryptedString("apps." + appCode)
	if err != nil {
		return ""
	}
	return decryptedString
}

func getSortedParam(querys map[string]string) string {
	names := make([]string, 0, len(querys))
	for name := range querys {
		names = append(names, name)
	}
	sort.Strings(names)
	var b bytes.Buffer
	for idx, name := range names {
		prefix := ""
		if idx != 0 {
			prefix = "&"
		}
		b.WriteString(prefix + name + "=" + querys[name])
	}

	return b.String()
}

func TestMiddle(querys map[string]string) string {
	queryString := getSortedParam(querys)
	queryAndSecret := queryString + secretString("")
	md5val := xencoding.Md5str(queryAndSecret)
	return md5val
}
